﻿/************************************************************************************
 * xujiajun@yoozoo.com 御剑
 * 基于相机中心点，格子范围，LOD的容器策略
 ***********************************************************************************/

using WorldMapBase.Camera;
using WorldMapBase.Layer;
using Unity.Mathematics;
using UnityEngine;
using WorldMapBase.LOD;
using System.Collections.Generic;
using Yoozoo;
using Yoozoo.Tools.Debugger;
namespace WorldMapBase
{
    /// <summary>
    /// 基于相机中心点，格子范围，LOD的容器策略
    /// </summary>
    public class GridLodCameraLayerContainerStrategy : ILayerContainerStrategy, ILODListener
    {
        /// <summary>
        /// 视图范围
        /// </summary>
        private int4 m_ViewRect;

        /// <summary>
        /// 相机对象
        /// </summary>
        private ICamera m_Camera;

        /// <summary>
        /// 格子宽度（米）
        /// </summary>
        private int m_GridWidth = 4;

        /// <summary>
        /// 格子高度（米）
        /// </summary>
        private int m_GridHeight = 3;

        /// <summary>
        /// LOD等级和格子范围的映射
        /// </summary>
        private Dictionary<int, int2> m_LodGridRange = new Dictionary<int, int2>();

        /// <summary>
        /// 当前LOD等级
        /// </summary>
        private int m_LodLevel;

        /// <summary>
        /// 构造基于相机视野范围的容器策略
        /// </summary>
        /// <param name="camera">相机对象</param>
        public GridLodCameraLayerContainerStrategy(ICamera camera)
        {
            m_Camera = camera;

            // 为防止报错添加的默认参数
            for (int i = 0; i < 10; i++)
            {
                m_LodGridRange.Add(i, new int2() { x = 16 * (i + 1), y = 16 * (i + 1) });
            }
        }

        /// <summary>
        /// 设置格子尺寸
        /// </summary>
        /// <param name="width">格子宽度（米）</param>
        /// <param name="height">格子高度（米）</param>
        public void SetGridSize(int width, int height)
        {
            m_GridWidth = width;
            m_GridHeight = height;
        }

        /// <summary>
        /// 设置指定LOD等级包含的格子范围
        /// </summary>
        /// <param name="lodLevel">LOD等级</param>
        /// <param name="gridRow">格子列数量</param>
        /// <param name="gridCol">格子行数量</param>
        public void AddLODGridRange(int lodLevel, int gridRow, int gridCol)
        {
            if (m_LodGridRange.ContainsKey(lodLevel))
            {
                m_LodGridRange[lodLevel] = new int2() { x = gridRow, y = gridCol };
            }
            else
            {
                m_LodGridRange.Add(lodLevel, new int2() { x = gridRow, y = gridCol });
            }
        }

        public int CompareLayer(int layer1, int layer2)
        {
            return layer1 < layer2 ? -1 : (layer1 > layer2 ? 1 : 0);
        }

        private float GetMax(float a, float b,float c,float d)
        {
            var max = Mathf.Max(a, b);
            max = Mathf.Max(max, c);
            max = Mathf.Max(max, d);
            return max;
        }
        
        private float GetMin(float a, float b,float c,float d)
        {
            var min = Mathf.Min(a, b);
            min = Mathf.Min(min, c);
            min = Mathf.Min(min, d);
            return min;
        }
        
        private Vector2 leftDir;
        private Vector2 rightDir;
        private Vector2 topDir;
        private Vector2 bottomDir;

        private bool directionInited;
        
        private void InitDirection()
        {
            var leftDirection = Quaternion.AngleAxis(-45, Vector3.up) * Vector3.forward;
            var topDirection = Quaternion.AngleAxis(45, Vector3.up) * Vector3.forward;
            leftDir = new Vector2(leftDirection.x, leftDirection.z);
            rightDir = - new Vector2(leftDirection.x, leftDirection.z);
            topDir = new Vector2(topDirection.x, topDirection.z);
            bottomDir = - new Vector2(topDirection.x, topDirection.z);
        }
        
        public int4 GetViewRect()
        {
            if (m_GridWidth == 0)
            {
                Debug.LogError("格子尺寸不能为0");
                return m_ViewRect;
            }
            else if (!m_LodGridRange.ContainsKey(m_LodLevel))
            {
                Debug.LogError("该LOD的范围不存在");
                return m_ViewRect;
            }
            else
            {
                if (!directionInited)
                {
                    directionInited = true;
                    InitDirection();
                }
                var center = m_Camera.GetCameraCenter();
                var gridRange = m_LodGridRange[m_LodLevel];
                
                var leftPos = center + leftDir * (gridRange.x >> 1) * m_GridWidth;
                var rightPos = center + rightDir * (gridRange.x >> 1) * m_GridWidth;
                var topPos = center + topDir * (gridRange.y >> 1) * m_GridHeight;
                var bottomPos = center + bottomDir * (gridRange.y >> 1) * m_GridHeight;

                var left = Mathf.FloorToInt(GetMin(leftPos.x, rightPos.x, topPos.x, bottomPos.x));
                var right = Mathf.CeilToInt(GetMax(leftPos.x,rightPos.x, topPos.x, bottomPos.x));
                var top = Mathf.CeilToInt(GetMax(leftPos.y, rightPos.y, topPos.y, bottomPos.y));
                var bottom = Mathf.FloorToInt(GetMin(leftPos.y, rightPos.y, topPos.y, bottomPos.y));

                m_ViewRect.x = (int)left;
                m_ViewRect.y = (int)bottom;
                m_ViewRect.z = (int)(right - left);
                m_ViewRect.w = (int)(top - bottom);
                
#if UNITY_EDITOR
                var leftBottom = new Vector3(left, 0, bottom);
                var rightBottom = new Vector3(right, 0, bottom);
                var leftTop = new Vector3(left, 0, top);
                var rightTop = new Vector3(right, 0, top);
               Debug.DrawLine(leftBottom,leftBottom + Vector3.up * 50,Color.green);
               Debug.DrawLine(rightBottom,rightBottom + Vector3.up * 50,Color.green);
               Debug.DrawLine(leftTop,leftTop + Vector3.up * 50,Color.green);
               Debug.DrawLine(rightTop,rightTop + Vector3.up * 50,Color.green);
             
#endif
                return m_ViewRect;
            }
        }

        public void OnLODLevelChange(int currentLodLevel, int lastLodLevel)
        {
            m_LodLevel = currentLodLevel;
        }

        public void OnNextLODPercentUpdate(int lodLevel, float nextLevelPercent)
        {

        }
      
    }

}
